// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using SnapObjects.Commons;
using System.Threading;
using System.Threading.Tasks;

namespace SnapObjects.Data
{
    /// <summary>
    ///     qlModelMapper is a transaction-oriented data manipulation component compliant with .NET Standard. 
    ///     It can build models with data from multiple tables, support nested levels of relationships, and 
    ///     provide explicit and implicit transaction management for business entities (i.e. a top-level object
    ///     and all of its children).When SqlModelMapper is used to save data, all database operations that 
    ///     have been tracked are automatically included in the transaction scope to ensure data integrity, it
    ///     can automatically commit all tracked data operations in one transaction to achieve data integrity.
    /// </summary>
    public class SqlModelMapper : ISqlModelMapper
    {
        private readonly DataContext _context;
        private readonly IModelLoader _modelLoader;
        private readonly IMapperContext _mapperContext;
        private readonly IMapperChangeTracker _changeTracker;

        /// <summary>
        ///     Initializes a new instance of the SqlModelMapper class with a DataContext object.
        /// </summary>
        /// <param name="context">A DataContext object, which includes the context information for creating a database session.</param>
        public SqlModelMapper(DataContext context)
        {
            _context = context;
            _mapperContext = new MapperContext(this);
            _modelLoader = new ModelLoader(context);
            _changeTracker = new MapperChangeTracker(_mapperContext);
        }

        #region ModelLoader
        /// <summary>
        ///     Retrieves data according to the SQL query defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.
        /// </param>
        /// <returns>Returns a <see cref="ILoadable{TModel}"/> 
        ///     object whose methods can be used to further obtain the result set, or perform data operations 
        ///     such as embedded queries.
        /// </returns>
        public ILoadable<TModel> Load<TModel>(params object[] parameters)
        {
            return _modelLoader.Load<TModel>(parameters);
        }

        /// <summary>
        ///     Asynchronously retrieves data according to the SQL query defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation. 
        /// </returns>
        public Task<ILoadable<TModel>> LoadAsync<TModel>(params object[] parameters)
        {
            return _modelLoader.LoadAsync<TModel>(parameters, default);
        }

        /// <summary>
        ///     Asynchronously retrieves data according to the SQL query defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.
        /// </param>
        /// <param name="cancellationToken">
        ///      A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation. 
        /// </returns>
        public Task<ILoadable<TModel>> LoadAsync<TModel>(object[] parameters, CancellationToken cancellationToken)
        {
            return _modelLoader.LoadAsync<TModel>(parameters, cancellationToken);
        }

        /// <summary>
        ///     Retrieves data for the specified page according to the SQL query defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="currentIndex">The zero-based index of the first record in the page.</param>
        /// <param name="pageSize">The count of rows in each page.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.
        /// </param>
        /// <returns>
        ///     Returns the <see cref="ILoadable{TModel}"/> interface whose methods can be used to further obtain the 
        ///     result set, or perform data operations such as embedded queries.
        /// </returns>
        public ILoadable<TModel> LoadByPage<TModel>(int currentIndex, int pageSize, params object[] parameters)
        {
            return _modelLoader.LoadByPage<TModel>(currentIndex, pageSize, parameters);
        }

        /// <summary>
        ///     Asynchronously retrieves data for the specified page according to the SQL query defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="currentIndex">The zero-based index of the first record in the page.</param>
        /// <param name="pageSize">The count of rows in each page.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<ILoadable<TModel>> LoadByPageAsync<TModel>(int currentIndex, int pageSize, params object[] parameters)
        {
            return _modelLoader.LoadByPageAsync<TModel>(currentIndex, pageSize, parameters, default);
        }

        /// <summary>
        ///     Asynchronously retrieves data for the specified page according to the SQL query defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="currentIndex">The zero-based index of the first record in the page.</param>
        /// <param name="pageSize">The count of rows in each page.</param>
        /// <param name="parameters">
        ///     One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.
        /// </param>
        /// <param name="cancellationToken">
        ///      A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<ILoadable<TModel>> LoadByPageAsync<TModel>(
            int currentIndex, int pageSize, object[] parameters, CancellationToken cancellationToken)
        {
            return _modelLoader.LoadByPageAsync<TModel>(currentIndex, pageSize, parameters, cancellationToken);
        }

        /// <summary>
        ///     Retrieves data from the database according to the SQL query (without the Where clause) defined by 
        ///     the primary table in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <returns>
        ///     Returns the <see cref="ILoadable{TModel}"/> interface whose methods can be used to further obtain the 
        ///     result set, or perform data operations such as embedded queries.
        /// </returns>
        public ILoadable<TModel> LoadAll<TModel>()
        {
            return _modelLoader.LoadAll<TModel>();
        }

        /// <summary>
        ///     Asynchronously retrieves data from the database according to the SQL query (without the Where clause) defined by 
        ///     the primary table in a TModel class.   
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <returns>Returns a task that represents the asynchronous operation.</returns>
        public Task<ILoadable<TModel>> LoadAllAsync<TModel>()
        {
            return _modelLoader.LoadAllAsync<TModel>(default);
        }

        /// <summary>
        ///     Asynchronously retrieves data from the database according to the SQL query (without the Where clause) defined by 
        ///     the primary table in a TModel class.   
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="cancellationToken">
        ///     A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>Returns a task that represents the asynchronous operation.</returns>
        public Task<ILoadable<TModel>> LoadAllAsync<TModel>(CancellationToken cancellationToken)
        {
            return _modelLoader.LoadAllAsync<TModel>(cancellationToken);
        }

        /// <summary>
        ///     Retrieves data for the specified page according to the SQL query (without the Where clause) 
        ///     defined by the primary table in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="currentIndex">The zero-based index of the first record in the page.</param>
        /// <param name="pageSize">The count of rows in each page.</param>
        /// <returns>
        ///     Returns the <see cref="ILoadable{TModel}"/> interface whose methods can be used to further obtain the 
        ///     result set, or perform data operations such as embedded queries.
        /// </returns>
        public ILoadable<TModel> LoadAllByPage<TModel>(int currentIndex, int pageSize)
        {
            return _modelLoader.LoadAllByPage<TModel>(currentIndex, pageSize);
        }

        /// <summary>
        ///     Asynchronously retrieves data for the specified page according to the SQL query (without the Where clause) 
        ///     defined by the primary table in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="currentIndex">The zero-based index of the first record in the page.</param>
        /// <param name="pageSize">The count of rows in each page.</param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<ILoadable<TModel>> LoadAllByPageAsync<TModel>(int currentIndex, int pageSize)
        {
            return _modelLoader.LoadAllByPageAsync<TModel>(currentIndex, pageSize, default);
        }

        /// <summary>
        ///     Asynchronously retrieves data for the specified page according to the SQL query (without the Where clause) 
        ///     defined by the primary table in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="currentIndex">The zero-based index of the first record in the page.</param>
        /// <param name="pageSize">The count of rows in each page.</param>
        /// <param name="cancellationToken">
        ///     A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<ILoadable<TModel>> LoadAllByPageAsync<TModel>(
            int currentIndex, int pageSize, CancellationToken cancellationToken)
        {
            return _modelLoader.LoadAllByPageAsync<TModel>(currentIndex, pageSize, cancellationToken);
        }

        /// <summary>
        ///     Retrieves data according to the primary key defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT statement defined by 
        ///     the primary table and its primary key(s) in TModel.
        /// </param>
        /// <returns>
        ///     Returns the <see cref="ILoadable{TModel}"/> interface whose methods can be used to further obtain the result set, or perform 
        ///     data operations such as embedded queries.
        /// </returns>
        public ILoadable<TModel> LoadByKey<TModel>(params object[] parameters)
        {
            return _modelLoader.LoadByKey<TModel>(parameters);
        }

        /// <summary>
        ///     Asynchronously retrieves data according to the primary key defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     One or more values that you want to use as retrieval arguments in the SQL SELECT statement defined by 
        ///     the primary table and its primary key(s) in TModel.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<ILoadable<TModel>> LoadByKeyAsync<TModel>(params object[] parameters)
        {
            return _modelLoader.LoadByKeyAsync<TModel>(parameters, default);
        }

        /// <summary>
        ///     Asynchronously retrieves data according to the primary key defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     One or more values that you want to use as retrieval arguments in the SQL SELECT statement defined by 
        ///     the primary table and its primary key(s) in TModel.
        /// </param>
        /// <param name="cancellationToken">
        ///     A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<ILoadable<TModel>> LoadByKeyAsync<TModel>(object[] parameters, CancellationToken cancellationToken)
        {
            return _modelLoader.LoadByKeyAsync<TModel>(parameters, cancellationToken);
        }

        /// <summary>
        ///     Obtains an <see cref="IEmbeddedLoadable{TModel}"/> object, which can be used to load the embedded property 
        ///     of a TModel instance.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">
        ///     A TModel instance which has defined one or more embedded properties (by applying the ModelEmbedded 
        ///     attribute or the SqlEmbedded attribute).
        /// </param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.These parameters will not be used to retrieve data for all properties 
        ///     in TModel object, but used to process the embedded property), because the embedded property) 
        ///     may have reference to these TModel parameters.
        /// </param>
        /// <returns>Returns an interface that can load one or more embedded properties) of a TModel object.</returns>
        public IEmbeddedLoadable<TModel> LoadEmbedded<TModel>(TModel model, params object[] parameters)
        {
            return _modelLoader.LoadEmbedded(model, parameters);
        }

        /// <summary>
        ///     Checks whether any record exists according to the retrieval criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments 
        ///     in the SQL SELECT statement defined in TModel.
        /// </param>
        /// <returns>Returns whether any record exists.</returns>
        public bool Exists<TModel>(params object[] parameters)
        {
            return _modelLoader.Exists<TModel>(parameters);
        }

        /// <summary>
        ///     Checks whether data exists when retrieved, according to the primary key defined in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT statement 
        ///     defined by the main table and its primary key in TModel.
        /// </param>
        /// <returns>Returns whether data exists.</returns>
        public bool KeyExists<TModel>(params object[] parameters)
        {
            return _modelLoader.KeyExists<TModel>(parameters);
        }

        /// <summary>
        ///     Gets the number of rows in the result set retrieved, according to the criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT statement 
        ///     defined in TModel.
        /// </param>
        /// <returns>Returns the number of rows in the database result set.</returns>
        public int Count<TModel>(params object[] parameters)
        {
            return _modelLoader.Count<TModel>(parameters);
        }

        /// <summary>
        ///     Gets the number of all distinct values specified by the SQL expression, 
        ///     according to the criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="expression">A string of SQL expression used to clear up the duplicate data.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT statement defined in TModel.
        /// </param>
        /// <returns>Returns the number of rows of the database result set, where duplicates have been cleared up.</returns>
        public int DistinctCount<TModel>(string expression, params object[] parameters)
        {
            return _modelLoader.DistinctCount<TModel>(expression, parameters);
        }

        /// <summary>
        ///     Gets the maximum value for the specified SQL expression, according to the retrieval 
        ///     criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="expression">A SQL expression string used to compute the maximum value.</param>
        /// <param name="parameters">
        /// (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT 
        /// statement defined in TModel.
        /// </param>
        /// <returns>
        ///     The maximum value of the retrieved data. Its data type is determined by the return value of the SQL expression.
        /// </returns>
        public object Max<TModel>(string expression, params object[] parameters)
        {
            return _modelLoader.Max<TModel>(expression, parameters);
        }

        /// <summary>
        ///     Gets the minimum value for the specified SQL expression, according to the retrieval criteria 
        ///     specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="expression">A SQL expression string used to compute the minimum value.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the 
        ///     SQL SELECT statement defined in TModel.
        /// </param>
        /// <returns>
        ///     The minimum value of the retrieved data. Its data type is determined by the return value of the SQL expression.
        /// </returns>
        public object Min<TModel>(string expression, params object[] parameters)
        {
            return _modelLoader.Min<TModel>(expression, parameters);
        }

        /// <summary>
        ///     Computes the average value using the specified SQL expression for the data 
        ///     retrieved according to the criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="expression">A string of SQL expression used to compute the average value.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL SELECT 
        ///     statement defined in TModel.
        /// </param>
        /// <returns>
        ///     The average value of the retrieved data. Its data type is determined by the return value of the SQL expression.
        /// </returns>
        public object Avg<TModel>(string expression, params object[] parameters)
        {
            return _modelLoader.Avg<TModel>(expression, parameters);
        }

        /// <summary>
        ///     Computes the sum of the value using the specified SQL expression for the data retrieved 
        ///     according to the criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="expression">A string of SQL expression used to compute the sum of the value.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL 
        ///     SELECT statement defined in TModel.
        /// </param>
        /// <returns>
        ///     The sum of the value of the retrieved data. Its data type is determined by the return value of the SQL expression.
        /// </returns>
        public object Sum<TModel>(string expression, params object[] parameters)
        {
            return _modelLoader.Sum<TModel>(expression, parameters);
        }

        /// <summary>
        ///     Retrieves data from the first column, in the first row, for the specified SQL expression, 
        ///     according to the criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <typeparam name="TValue">The type of the value retrieved.</typeparam>
        /// <param name="expression">
        ///     A SQL expression string used to get data from the first column in the first row.
        /// </param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL 
        ///     SELECT statement defined in TModel.
        /// </param>
        /// <returns>
        ///     Returns the data from the first column in the first row, of the retrieved data. Its 
        ///     data type is determined by the return value of the SQL expression.
        /// </returns>
        public TValue Scalar<TModel, TValue>(string expression, params object[] parameters)
        {
            return (TValue)_modelLoader.FetchOne<TModel>(expression, parameters);
        }

        /// <summary>
        ///     Asynchronously retrieves data from the first column, in the first row, for the specified SQL expression, 
        ///     according to the criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <typeparam name="TValue">The type of the value retrieved.</typeparam>
        /// <param name="expression">
        ///     A SQL expression string used to get data from the first column in the first row.
        /// </param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL 
        ///     SELECT statement defined in TModel.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<TValue> ScalarAsync<TModel, TValue>(string expression, params object[] parameters)
        {
            return _modelLoader.FetchOneAsync<TModel, TValue>(expression, parameters, default);
        }

        /// <summary>
        ///     Asynchronously retrieves data from the first column, in the first row, for the specified SQL expression, 
        ///     according to the criteria specified in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <typeparam name="TValue">The type of the value retrieved.</typeparam>
        /// <param name="expression">
        ///     A SQL expression string used to get data from the first column in the first row.
        /// </param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL 
        ///     SELECT statement defined in TModel.
        /// </param>
        /// <param name="cancellationToken">
        ///     A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<TValue> ScalarAsync<TModel, TValue>(
            string expression, object[] parameters, CancellationToken cancellationToken)
        {
            return _modelLoader.FetchOneAsync<TModel, TValue>(expression, parameters, cancellationToken);
        }

        /// <summary>
        ///     Retrieves data from the first column, in the first row, for the specified SQL 
        ///     expression according to the primary key in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <typeparam name="TValue">The type of the value retrieved.</typeparam>
        /// <param name="expression">
        ///     A SQL expression string used to get data from the first column in the first row.
        /// </param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as retrieval arguments in the SQL 
        ///     SELECT statement defined by the primary table and the primary key(s) in TModel.
        /// </param>
        /// <returns>
        ///     Returns the data from the first column in the first row of the retrieved data. 
        ///     Its data type is determined by the return value of the SQL expression.
        /// </returns>
        public TValue ScalarByKey<TModel, TValue>(string expression, params object[] parameters)
        {
            return (TValue)_modelLoader.FetchOneByKey<TModel>(expression, parameters);
        }

        /// <summary>
        ///     Asynchronously retrieves data from the first column, in the first row, for the specified SQL 
        ///     expression according to the primary key in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <typeparam name="TValue">The type of the value retrieved.</typeparam>
        /// <param name="expression">
        ///     A SQL expression string used to get data from the first column in the first row.
        /// </param>
        /// <param name="parameters">
        ///     One or more values that you want to use as retrieval arguments in the SQL 
        ///     SELECT statement defined by the primary table and the primary key(s) in TModel.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<TValue> ScalarByKeyAsync<TModel, TValue>(string expression, params object[] parameters)
        {
            return _modelLoader.FetchOneByKeyAsync<TModel, TValue>(expression, parameters, default);
        }

        /// <summary>
        ///     Asynchronously retrieves data from the first column, in the first row, for the specified SQL 
        ///     expression according to the primary key in a TModel class.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <typeparam name="TValue">The type of the value retrieved.</typeparam>
        /// <param name="expression">
        ///     A SQL expression string used to get data from the first column in the first row.
        /// </param>
        /// <param name="parameters">
        ///     One or more values that you want to use as retrieval arguments in the SQL 
        ///     SELECT statement defined by the primary table and the primary key(s) in TModel.
        /// </param>
        /// <param name="cancellationToken">
        ///     A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<TValue> ScalarByKeyAsync<TModel, TValue>(
            string expression, object[] parameters, CancellationToken cancellationToken)
        {
            return _modelLoader.FetchOneByKeyAsync<TModel, TValue>(expression, parameters, cancellationToken);
        }
        #endregion

        /// <summary>
        ///     Tracks an <see cref="Action{ISaveContext}"/> object. The Action will be called when 
        ///     ISqlModelMapper.SaveChanges method is called.
        /// </summary>
        /// <param name="saveAction">
        ///     An <see cref="Action{ISaveContext}"/> object that needs to be tracked. Please avoid calling any t
        ///     racking method of the current ISqlModelMapper object in the script of the Action.
        /// </param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper Track(Action<ISaveContext> saveAction)
        {
            Check.NotNull(saveAction, nameof(saveAction));

            _changeTracker.Track(saveAction);

            return this;
        }

        /// <summary>
        ///     Tracks an IMapperTrackable object. When ISqlModelMapper.SaveChanges method is called, 
        ///     the SaveChanges method of this object will be called.
        /// </summary>
        /// <param name="mapperTrackable">The IMapperTrackable object to be tracked.</param>
        /// <param name="saveParms">The parameters used by the SaveChanges method of IMapperTrackable.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used to execute other methods.</returns>
        public ISqlModelMapper Track(IMapperTrackable mapperTrackable, params object[] saveParms)
        {
            Check.NotNull(mapperTrackable, nameof(mapperTrackable));

            _changeTracker.Track(mapperTrackable, saveParms);

            return this;
        }

        #region TrackModel
        internal IModelEntry GetModelEntry<TModel>(TModel model)
        {
            Check.NotNull(model, nameof(model));

            return _changeTracker.GetModelEntry(model);
        }

        /// <summary>
        ///     Tracks a database table operation (insert, update, or delete). The data state determines which type of 
        ///     operation (insert, update, or delete) to be performed. The data to be manipulated is cached in the 
        ///     TModel object. When ISqlModelMapper.SaveChanges method is called, a SQL statement (INSERT, UPDATE or DELETE) 
        ///     will be first generated using the data cached in the TModel object and the mapping information defined 
        ///     in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="modelEntry">
        ///     An <see cref="IModelEntry{TModel}"/> object including data and state.
        /// </param>
        /// <returns>Returns a tracked model object which includes the data from the <see cref="IModelEntry{TModel}"/> object.</returns>
        public TModel Track<TModel>(
            IModelEntry<TModel> modelEntry)
        {
            Check.NotNull(modelEntry, nameof(modelEntry));

            return _changeTracker.Track(modelEntry, false, null, null);
        }

        /// <summary>
        ///     Tracks a database table operation (insert, update, or delete) and an <see cref="Action{ISaveContext}"/> object. 
        ///     The data state determines which type of operation (insert, update, or delete) to be performed. 
        ///     The data to be manipulated is cached in the TModel object. When ISqlModelMapper.SaveChanges method 
        ///     is called, a SQL statement (INSERT, UPDATE or DELETE) will be first generated using the data cached 
        ///     in the TModel object and the mapping information defined in TModel class, and then executed; 
        ///     and after that, the <see cref="Action{ISaveContext}"/> object will be called.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="modelEntry">An <see cref="IModelEntry{TModel}"/> object including data and state.</param>
        /// <param name="afterSaveAction">An <see cref="Action{ISaveContext}"/> object that needs to be tracked.</param>
        /// <returns>
        ///     Returns a tracked TModel instance which includes the data from the <see cref="IModelEntry{TModel}"/> object.
        /// </returns>
        public TModel Track<TModel>(
            IModelEntry<TModel> modelEntry,
            Action<ISaveContext> afterSaveAction)
        {
            Check.NotNull(modelEntry, nameof(modelEntry));

            return _changeTracker.Track(modelEntry, false, null, afterSaveAction);
        }

        /// <summary>
        ///     Tracks an insert, update or delete operation to the master table, when working with 
        ///     multiple models which are in the master-detail relationship. The data state determines 
        ///     which type of operation (insert, update or delete) will be performed. The data to be 
        ///     manipulated is cached in the TModel object. When ISqlModelMapper.SaveChanges method is 
        ///     called, a SQL statement (INSERT, UPDATE or DELETE) will be first generated using the 
        ///     data cached in the TModel object and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="modelEntry">A <see cref="IModelEntry{TModel}"/> object which includes the data and state of the master model.</param>
        /// <returns>An <see cref="IDetailTracker{TModel}"/> object that can be used to track the detail model.</returns>
        public IDetailTracker<TModel> TrackMaster<TModel>(
            IModelEntry<TModel> modelEntry)
        {
            Check.NotNull(modelEntry, nameof(modelEntry));

            return this.TrackMaster(modelEntry, null, null);
        }

        /// <summary>
        ///     Tracks an insert, update or delete operation to the master table and an <see cref="Action{ISaveContext}"/> object, 
        ///     when working with multiple models which are in the master-detail relationship. The data state 
        ///     determines which type of operation (insert, update or delete) will be performed. The data to be 
        ///     manipulated is cached in the TModel object. When ISqlModelMapper.SaveChanges method is called, 
        ///     a SQL statement (INSERT, UPDATE or DELETE) will be first generated using the data cached in the 
        ///     TModel object and the mapping information defined in TModel class, and then executed; and after that,
        ///     the <see cref="Action{ISaveContext}"/> object will be called.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="modelEntry">An <see cref="IModelEntry{TModel}"/> object which includes the data and state of the master model.</param>
        /// <param name="afterSaveAction">An <see cref="Action{ISaveContext}"/> object that needs to be tracked.</param>
        /// <returns>An interface that can be used to track the detail model.</returns>
        public IDetailTracker<TModel> TrackMaster<TModel>(
            IModelEntry<TModel> modelEntry,
            Action<ISaveContext> afterSaveAction)
        {
            Check.NotNull(modelEntry, nameof(modelEntry));

            return this.TrackMaster(modelEntry, null, afterSaveAction);
        }

        private IDetailTracker<TModel> TrackMaster<TModel>(
            IModelEntry<TModel> modelEntry,
            Action<ISaveContext> beforeSaveAction,
            Action<ISaveContext> afterSaveAction)
        {
            Check.NotNull(modelEntry, nameof(modelEntry));

            var trackable = new ModelTrackable<TModel>(_mapperContext, _changeTracker);
            var trackDetail = trackable.TrackMaster(modelEntry, beforeSaveAction, afterSaveAction);

            _changeTracker.AddTracker(trackable.MasterModel, trackable.MasterEntryTracker);

            return trackDetail;
        }

        /// <summary>
        ///     Tracks a database table insert operation. The data that will be inserted into the 
        ///     database table is cached in the TModel object. When ISqlModelMapper.SaveChanges 
        ///     method is called, a SQL INSERT statement will be first generated using the data 
        ///     cached in the TModel object and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">A TModel instance used for adding a record.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackCreate<TModel>(TModel model)
        {
            Check.NotNull(model, nameof(model));

            _changeTracker.TrackCreate(model, null, null);

            return this;
        }

        /// <summary>
        ///     Tracks a database table insert operation and an <see cref="Action{ISaveContext}"/> object. 
        ///     The data that will be inserted into the database table is cached in the TModel 
        ///     object. When ISqlModelMapper.SaveChanges method is called, a SQL INSERT statement 
        ///     will be first generated using the data cached in the TModel object and the 
        ///     mapping information defined in TModel class, and then executed. After that, 
        ///     the <see cref="Action{ISaveContext}"/> object will be called.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">A model instance used for adding a record.</param>
        /// <param name="afterSaveAction">An <see cref="Action{ISaveContext}"/> object that needs to be tracked.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackCreate<TModel>(
            TModel model,
            Action<ISaveContext> afterSaveAction)
        {
            Check.NotNull(model, nameof(model));

            _changeTracker.TrackCreate(model, null, afterSaveAction);

            return this;
        }

        /// <summary>
        ///     Tracks a database table update operation. The data that will be updated to the database 
        ///     table is cached in the TModel object. When ISqlModelMapper.SaveChanges method is called, 
        ///     a SQL UPDATE statement will be first generated using the data cached in the TModel object 
        ///     and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">The TModel object that contains the data you want to update to database.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackUpdate<TModel>(TModel model)
        {
            Check.NotNull(model, nameof(model));

            _changeTracker.TrackUpdate(model, null, null);

            return this;
        }

        /// <summary>
        ///     Tracks a database table update operation and an <see cref="Action{ISaveContext}"/> object. The data that 
        ///     will be updated to the database table is cached in the TModel object. When ISqlModelMapper.SaveChanges 
        ///     method is called, a SQL UPDATE statement will be first generated using the data cached in the 
        ///     TModel object and the mapping information defined in TModel class, and then executed. After that, 
        ///     the <see cref="Action{ISaveContext}"/> object will be called.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">The TModel object that contains the data you want to update to database.</param>
        /// <param name="afterSaveAction">An <see cref="Action{ISaveContext}"/> object that needs to be tracked.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackUpdate<TModel>(
            TModel model,
            Action<ISaveContext> afterSaveAction)
        {
            Check.NotNull(model, nameof(model));

            _changeTracker.TrackUpdate(model, null, afterSaveAction);

            return this;
        }

        /// <summary>
        ///     Tracks a database table update operation. The data that will be updated to the database table 
        ///     is cached in the modifiedModel and the original data is cached in the originalModel. 
        ///     When ISqlModelMapper.SaveChanges method is called, a SQL UPDATE statement will be first generated 
        ///     using the data cached in the modifiedModel , the data cached in the originalModel, and the mapping 
        ///     information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="originalModel">The TModel object that contains the original data you want to update.</param>
        /// <param name="modifiedModel">The TModel object that contains the modified data you want to update to database.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackUpdate<TModel>(TModel originalModel, TModel modifiedModel)
        {
            Check.NotNull(originalModel, nameof(originalModel));
            Check.NotNull(modifiedModel, nameof(modifiedModel));

            _changeTracker.TrackUpdate(originalModel, modifiedModel, null, null);

            return this;
        }

        /// <summary>
        ///     Tracks a database table update operation and an <see cref="Action{ISaveContext}"/> object. The data that will be 
        ///     updated to the database table is cached in the modifiedModel and the original data is cached in 
        ///     the originalModel. When ISqlModelMapper.SaveChanges method is called, a SQL UPDATE statement will 
        ///     be first generated using the data cached in the modifiedModel , the data cached in the originalModel, 
        ///     and the mapping information defined in TModel class, and then executed. After that, 
        ///     the <see cref="Action{ISaveContext}"/> object will be called.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="originalModel">The TModel object that contains the original data you want to update.</param>
        /// <param name="modifiedModel">The TModel object that contains the modified data you want to update to database.</param>
        /// <param name="afterSaveAction">An <see cref="Action{ISaveContext}"/> object that needs to be tracked.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackUpdate<TModel>(
            TModel originalModel,
            TModel modifiedModel,
            Action<ISaveContext> afterSaveAction)
        {
            Check.NotNull(originalModel, nameof(originalModel));
            Check.NotNull(modifiedModel, nameof(modifiedModel));

            _changeTracker.TrackUpdate(originalModel, modifiedModel, null, afterSaveAction);

            return this;
        }

        /// <summary>
        ///     Tracks a database table delete operation. The data that will be deleted from the database 
        ///     table is cached in the TModel object. When ISqlModelMapper.SaveChanges method is called, 
        ///     a SQL DELETE statement will be first generated using the data cached in the TModel object 
        ///     and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">The TModel instance that corresponds to the database record.</param>
        /// <returns>Returns an <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackDelete<TModel>(TModel model)
        {
            Check.NotNull(model, nameof(model));

            _changeTracker.TrackDelete(model, null, null);

            return this;
        }

        /// <summary>
        ///     Tracks a database table delete operation and an <see cref="Action{ISaveContext}"/> object. The data that will 
        ///     be deleted from the database table is cached in the TModel object. When ISqlModelMapper.SaveChanges 
        ///     method is called, a SQL DELETE statement will be first generated using the data cached in the 
        ///     TModel object and the mapping information defined in TModel class, and then executed; and after that, 
        ///     the <see cref="Action{ISaveContext}"/> object will be called.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">The TModel instance that corresponds to the database record.</param>
        /// <param name="afterSaveAction">An <see cref="Action{ISaveContext}"/> object that needs to be tracked.</param>
        /// <returns>Returns an <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackDelete<TModel>(
            TModel model,
            Action<ISaveContext> afterSaveAction)
        {
            Check.NotNull(model, nameof(model));

            _changeTracker.TrackDelete(model, null, afterSaveAction);

            return this;
        }

        /// <summary>
        ///     Tracks a database table delete operation by the primary key. A SQL DELETE statement will
        ///     be generated using the primary key value(s) and the mapping information defined in TModel. 
        ///     When ISqlModelMapper.SaveChanges method is called, the SQL DELETE statement will be executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="parameters">
        ///     (Optional) One or more primary key values that you want to use as arguments in the SQL DELETE statement.
        /// </param>
        /// <returns>Returns an <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackDeleteByKey<TModel>(params object[] parameters)
        {
            Check.NotNull(parameters, nameof(parameters));

            new ModelDeleter<TModel>(_context, _mapperContext, _changeTracker)
                .TrackDeleteByKey(parameters);

            return this;
        }

        /// <summary>
        ///     Tracks one or more database table operations (insert, update or delete). The data state 
        ///     determines which type of operation will be performed. The data to be manipulated is cached 
        ///     in the TModel objects. When ISqlModelMapper.SaveChanges method is called, one or more SQL
        ///     statements (INSERT, UPDATE or DELETE) will be generated using the data cached in the TModel 
        ///     objects and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="modelEntries">A sequence of <see cref="IModelEntry{TModel}"/> objects containing data and state.</param>
        /// <returns>A list of TModel objects which have been tracked by this method.</returns>
        public IList<TModel> TrackRange<TModel>(
            IEnumerable<IModelEntry<TModel>> modelEntries)
        {
            Check.NotNull(modelEntries, nameof(modelEntries));

            var sortModels = new List<IModelEntry<TModel>>();

            sortModels.AddRange(modelEntries.Where(m => m.ModelState == ModelState.Deleted));

            sortModels.AddRange(modelEntries.Where(m => m.ModelState == ModelState.Modified));

            sortModels.AddRange(modelEntries.Where(
                m => m.ModelState != ModelState.Modified && m.ModelState != ModelState.Deleted));

            var models = new List<TModel>();

            foreach (var modelEntry in sortModels)
            {
                if (modelEntry.ModelState != ModelState.NotTracked)
                {
                    models.Add(_changeTracker.Track(modelEntry, false, null, null));
                }
            }

            return models;
        }

        /// <summary>
        ///     Tracks one or more database table insert operations. The data that will be inserted into 
        ///     the database table is cached in the TModel object. When ISqlModelMapper.SaveChanges method 
        ///     is called, one or more SQL INSERT statements will be first generated using the data cached 
        ///     in the TModel object and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="models">A collection of TModel type used for adding one or multiple records.</param>
        /// <returns>Returns an <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackCreateRange<TModel>(IEnumerable<TModel> models)
        {
            Check.NotNull(models, nameof(models));

            foreach (var model in models)
            {
                _changeTracker.TrackCreate(model, null, null);
            }

            return this;
        }

        /// <summary>
        ///     Tracks one or more database table update operations. The data that will be updated to the
        ///     database table is cached in the TModel objects. When ISqlModelMapper.SaveChanges method is 
        ///     called, one or more SQL UPDATE statements will be first generated using the data cached in 
        ///     the TModel objects and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="models">A sequence of TModel objects used for updating one or multiple records in database.</param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackUpdateRange<TModel>(IEnumerable<TModel> models)
        {
            Check.NotNull(models, nameof(models));

            foreach (var model in models)
            {
                _changeTracker.TrackUpdate(model, null, null);
            }

            return this;
        }

        /// <summary>
        ///     Tracks one or more database table delete operations. The data that will be deleted from 
        ///     the database table is cached in the TModel object. When ISqlModelMapper.SaveChanges method 
        ///     is called, one or more SQL DELETE statements will be first generated using the data cached 
        ///     in the TModel object and the mapping information defined in TModel class, and then executed.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="models">A collection of TModel type used for deleting one or multiple records.</param>
        /// <returns>Returns an <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackDeleteRange<TModel>(IEnumerable<TModel> models)
        {
            Check.NotNull(models, nameof(models));

            foreach (var model in models)
            {
                _changeTracker.TrackDelete(model, null, null);
            }

            return this;
        }
        #endregion

        #region TrackSqlCUD
        /// <summary>
        ///     Tracks a database table operation (insert, update or delete) using a raw SQL statement (INSERT, UPDATE, or DELETE). 
        ///     When ISqlModelMapper.SaveChanges method is called, the SQL statement will be executed.
        /// </summary>
        /// <param name="sqlText">
        ///     An SQL INSERT, UPDATE, or DELETE statement.
        ///     Use : or @ symbol at the beginning to represent the parameter required by the SQL statement.
        /// </param>
        /// <param name="parameters">
        ///     The arguments required by the SQL statement. The order of arguments here must be the same as the order of arguments in sqlText.
        /// </param>
        /// <returns>Returns an ISqlModelMapper object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackSqlCUD(string sqlText, params object[] parameters)
        {
            Check.NotEmpty(sqlText, nameof(sqlText));

            _changeTracker.TrackSqlCUD(sqlText, parameters);

            return this;
        }

        /// <summary>
        ///     Tracks a database table insert operation. A SQL INSERT statement will be generated using the 
        ///     specified ISqlInsertBuilder object. When ISqlModelMapper.SaveChanges method is called, 
        ///     the SQL INSERT statement will be executed.
        /// </summary>
        /// <param name="insertBuilder">An ISqlInsertBuilder object used to build the SQL INSERT statement.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as arguments in the SQL INSERT statement defined in insertBuilder.
        /// </param>
        /// <returns>Returns the current <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackSqlBuilder(ISqlInsertBuilder insertBuilder, params object[] parameters)
        {
            Check.NotNull(insertBuilder, nameof(insertBuilder));

            _changeTracker.TrackSqlCUD(_context, insertBuilder, parameters);

            return this;
        }

        /// <summary>
        ///     Tracks a database table delete operation. A SQL DELETE statement will be generated using the specified 
        ///     ISqlDeleteBuilder object. When ISqlModelMapper.SaveChanges method is called, the SQL DELETE statement will be executed.
        /// </summary>
        /// <param name="deleteBuilder">An interface used to build the SQL DELETE statement.</param>
        /// <param name="parameters">
        ///     (Optional) One or more values that you want to use as arguments in the DELETE SQL statement defined in deleteBuilder.
        /// </param>
        /// <returns>Returns an <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackSqlBuilder(ISqlDeleteBuilder deleteBuilder, params object[] parameters)
        {
            Check.NotNull(deleteBuilder, nameof(deleteBuilder));

            _changeTracker.TrackSqlCUD(_context, deleteBuilder, parameters);

            return this;
        }

        /// <summary>
        ///     Tracks a database table update operation. A SQL UPDATE statement will be generated using the 
        ///     specified ISqlUpdateBuilder object. When ISqlModelMapper.SaveChanges method is called, 
        ///     the SQL UPDATE statement will be executed.
        /// </summary>
        /// <param name="updateBuilder">An ISqlUpdateBuilder object used to build the SQL UPDATE statement.</param>
        /// <param name="parameters">
        ///     One or more values that you want to use as arguments in the SQL UPDATE statement defined in updateBuilder.
        /// </param>
        /// <returns>Returns an <see cref="ISqlModelMapper"/> object, which can be used for executing other methods.</returns>
        public ISqlModelMapper TrackSqlBuilder(ISqlUpdateBuilder updateBuilder, params object[] parameters)
        {
            Check.NotNull(updateBuilder, nameof(updateBuilder));

            _changeTracker.TrackSqlCUD(_context, updateBuilder, parameters);

            return this;
        }
        #endregion

        /// <summary>
        ///     Performs validity check to all data in the model tracked by the current ISqlModelMapper object.
        /// </summary>
        /// <returns>Whether the data in the model tracked has passed the validity check.</returns>
        public bool ValidateTrackedModels()
        {
            var ValidationResults = _changeTracker.ValidateAll();

            return ValidationResults.Count == 0;
        }

        /// <summary>
        ///     Performs validity check to all data in the model tracked by the current <see cref="ISqlModelMapper"/> object and gets the validation result.
        /// </summary>
        /// <param name="ValidationResults">A <see cref="List{ValidationResult}"/> object that includes the data validation results.</param>
        /// <returns>Whether all of the data in the model object tracked has passed the validity check.</returns>
        public bool ValidateTrackedModels(out List<ValidationResult> ValidationResults)
        {
            ValidationResults = (List<ValidationResult>)_changeTracker.ValidateAll();

            return ValidationResults.Count == 0;
        }

        /// <summary>
        ///     Saves all of the data changes to the database, by executing the database table operations 
        ///     tracked by the ISqlModelMapper object, and calls the tracked Action delegates during operations.
        /// </summary>
        /// <returns>
        ///     Returns an IDbResult object whose property can be used to conveniently get the result of 
        ///     the executed database operation.
        /// </returns>
        public IDbResult SaveChanges()
        {
            return _changeTracker.Update(_context);
        }

        /// <summary>
        ///     Asynchronously saves all of the data changes to the database, by executing the database table operations 
        ///     tracked by the ISqlModelMapper object, and calls the tracked Action delegates during operations.
        /// </summary>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<IDbResult> SaveChangesAsync()
        {
            return _changeTracker.UpdateAsync(_context, default);
        }

        /// <summary>
        ///     Asynchronously saves all of the data changes to the database, by executing the database table operations 
        ///     tracked by the ISqlModelMapper object, and calls the tracked Action delegates during operations.
        /// </summary>
        /// <param name="cancellationToken">
        ///     A cancellation token that can be used by other objects or threads to receive notice of cancellation.
        /// </param>
        /// <returns>
        ///     Returns a task that represents the asynchronous operation.
        /// </returns>
        public Task<IDbResult> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            return _changeTracker.UpdateAsync(_context, cancellationToken);
        }

        /// <summary>
        ///     Clears all tracked information from a model.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <param name="model">The model object whose tracked information you want to remove.</param>
        /// <returns>
        ///     Returns true if the tracked information of the model object has been removed; 
        ///     returns false if tracked information of the model does not exist or the model is null.
        /// </returns>
        public bool RemoveTrackedModel<TModel>(TModel model)
        {
            return _changeTracker.RemoveTrackedModel(model);
        }

        /// <summary>
        ///     Clears all tracked information.
        /// </summary>
        public void ClearTracked()
        {
            _changeTracker.RemoveAllTracked();
        }

        /// <summary>
        ///     Gets the query conditions of a dynamic query.
        /// </summary>
        /// <typeparam name="TModel">The type of a model class.</typeparam>
        /// <returns>Returns a newly created IQueryBuilder object.</returns>
        public IQueryBuilder<TModel> GetQueryBuilder<TModel>(bool removeWhereClause = false)
        {
            return new QueryBuilder<TModel>(_context, removeWhereClause);
        }
    }
}
